package com.zjy.web.config;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zjy.common.domain.dto.RequestInfoDto;
import com.zjy.common.domain.model.RequestHolder;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author zhangjianyu
 * email: 578547270@qq.com
 * create_time: 2024-10-22 16:01
 * description: 日志
 */
@Slf4j
@RequiredArgsConstructor
@Aspect
@Component
public class LogAop {

    @Pointcut(value = "execution(* com.zjy..controller..*(..))")
    public void serviceStatistics() {
    }

    /**
     * 正常拦截
     *
     * @param pjd 处理方法
     * @return
     * @throws Throwable
     */
    @Around(value = "serviceStatistics()")
    public Object doAroundService(ProceedingJoinPoint pjd) throws Throwable {
        HttpServletRequest request = getHttpServletRequest();
        RequestInfoDto requestInfoDto = RequestHolder.get();

        JSONObject logInfo = new JSONObject();
        logInfo.set("uri", request.getRequestURI());
        logInfo.set("userInfo", requestInfoDto);
        logInfo.set("param", getRequestParam(pjd, request));
        Long startTime = System.currentTimeMillis();
        Object result = pjd.proceed();
        logInfo.set("executeTime", System.currentTimeMillis() - startTime);
        log.info("requestInfo：\n {}", JSONUtil.toJsonPrettyStr(logInfo));
        return result;
    }

    /**
     * 在抛出异常后执行的方法
     * 该方法用于处理服务统计切面中抛出的异常
     * 它捕获异常并记录有关请求和异常的详细信息
     *
     * @param joinPoint 切入点对象，用于获取方法参数等信息
     * @param e         抛出的异常对象
     */
    @AfterThrowing(pointcut = "serviceStatistics()", throwing = "e")
    public void handleThrowing(JoinPoint joinPoint, Exception e) {
        HttpServletRequest request = getHttpServletRequest();
        RequestInfoDto requestInfoDto = RequestHolder.get();
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        JSONObject logInfo = new JSONObject();
        logInfo.put("uri", request.getRequestURI());
        logInfo.put("userInfo", requestInfoDto);
        logInfo.put("param", getRequestParam(joinPoint, request));
        logInfo.put("error", e.getMessage());
        log.error("errorRequest：\n {}", JSONUtil.toJsonPrettyStr(logInfo));
    }

    /**
     * 获取request数据
     *
     * @return
     */
    private HttpServletRequest getHttpServletRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return attributes.getRequest();
    }

    /**
     * 解析参数
     *
     * @param pjd
     * @param request
     * @return
     */
    private Object getRequestParam(JoinPoint pjd, HttpServletRequest request) {
        Object params = null;
        if (!isMultipartContent(request)) {
            try {
                List<Object> paramList = Stream.of(pjd.getArgs())
                        .filter(args -> !(args instanceof ServletRequest))
                        .filter(args -> !(args instanceof ServletResponse))
                        .collect(Collectors.toList());
                if (paramList.size() == 1) {
                    Object data = paramList.get(0);
                    try {
                        JSONObject jsonObject = JSONUtil.parseObj(paramList.get(0));
                        if (jsonObject == null || jsonObject.isEmpty()) {
                            // json解析失败
                            return data;
                        }
                        return jsonObject;
                    } catch (Exception e) {
                        return data;
                    }
                } else {
                    return JSONUtil.parseArray(paramList);
                }
            } catch (Exception e) {
                log.error("参数解析失败", e);
            }
        }
        return params;
    }

    /**
     * 判断是否为上传文件
     *
     * @param request
     * @return
     */
    public static boolean isMultipartContent(HttpServletRequest request) {
        if (!"POST".equalsIgnoreCase(request.getMethod())) {
            return false;
        }
        String contentType = request.getContentType();
        if (StringUtils.isNotBlank(contentType)) {
            if (contentType.toLowerCase().startsWith("multipart/")) {
                return true;
            }
        }
        return false;
    }
}
